package com.yunmai.library.db;

import android.content.Context;
import android.support.annotation.Nullable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 数据库操作框架
 * <p>
 * <pre><code>
 * DBManager dbManager = new DBManager.Builder()
 *     .build();
 *
 * MyDatabase database = dbManager.create(MyDatabase.class);
 * Observable observable = database.getAll(context);
 * </code></pre>
 *
 * @author liyi
 *         create at 2018/3/26
 **/
public final class DBManager {

    private final Map<Method, DatabaseMethod<?, ?>> serviceMethodCache = new ConcurrentHashMap<>();
    private DBManagerAdapter dbManagerAdapter;

    /**
     * 创建一个操作数据库对象
     *
     * @param context
     * @param database
     * @param <T>
     * @return
     */
    public <T> T create(final Context context, final Class<T> database) {
        eagerlyValidateMethods(database);

        return (T) Proxy.newProxyInstance(database.getClassLoader(), new Class<?>[]{database},
            new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, final Method method, @Nullable
                final Object[] args)
                    throws Throwable {
                    // If the method is a method from Object then defer to normal invocation.
                    if (method.getDeclaringClass() == Object.class) {
                        return method.invoke(this, args);
                    }

                    final DatabaseMethod<Object, Object> databaseMethod =
                        (DatabaseMethod<Object, Object>) loadDatebaseMethod(database, method);
                    DBCall dbCall = new DBCall(context, databaseMethod, args);
                    return getDbManagerAdapter().aapt(dbCall);
                }
            });
    }

    /**
     * 获取数据库操作适配器，目前只支持RxJava
     *
     * @return
     */
    public DBManagerAdapter getDbManagerAdapter() {
        if (dbManagerAdapter == null) {
            dbManagerAdapter = new RxJavaAdapter<>();
        }
        return dbManagerAdapter;
    }

    private void eagerlyValidateMethods(Class<?> database) {
        for (Method method : database.getDeclaredMethods()) {
            loadDatebaseMethod(database, method);
        }
    }

    DatabaseMethod<?, ?> loadDatebaseMethod(Class<?> database, Method method) {
        DatabaseMethod<?, ?> result = serviceMethodCache.get(method);
        if (result != null) return result;

        synchronized (this) {
            result = serviceMethodCache.get(method);
            if (result == null) {
                result = new DatabaseMethod(database, this, method);
                serviceMethodCache.put(method, result);
            }
        }
        return result;
    }

    public static final class Builder {

        private String dbName;
        private int versionCode;

        public DBManager build() {
            return new DBManager();
        }

    }

}
